Google OAuth 2.0 登录流程
client向server发起
GET /auth/google
请求server将client重定向至google oauth登录页
https://accounts.google.com/o/oauth2/auth
,并附带以下查询参数集:response_type: code
登录后返回授权码client_id: GOOGLE_CLIENT_ID
Google控制台中的client_id,用于google识别应用程序redirect_url: http://www.example.com/auth/google/callback
回调URL,必须和Google控制台中设置的回调URL一致scope: ['profile', 'email']
用户授权使用的数据返回,显示在oauth登录确认页中
用户在登录页授权登录后,google api server会将client重定向至之前设定的回调URL,并附带authorization code授权码
server收到针对回调URL的GET请求
GET /auth/google/callback
,从中提取出code授权码,并使用该code向google api server换取access token请求地址:
https://www.googleapis.com/oauth2/v3/token
请求参数
- code: 之前获得的授权码
- grant_type: authorization_code:指明使用授权码进行验证
- client_id,同上
- client_secret,同上
- redirect_url,同上
返回值:
- access_token:用于获取其他google api数据或控制权
- expires_in:过期时间
- token_type:指明token类型
- id_token:一串加密信息,使用base64 decode解密后可以获得用户邮箱email和用户唯一标识符sub等信息
- refresh_token:access_token过期后,可以使用refresh_token来重新获取新的access_token,而不需要用户重新授权
可以将上述token信息保存到数据库,即使用户不使用app时也能获取用户信息和代替用户进行google api操作等
server从google api收到用户数据后,可以将用户注册到数据库,然后将用户信息序列化后设置cookie并返回给client,后续client使用该cookie进行身份验证
使用passport middleware可以帮助我们简化中间与google api交互的流程,我们只需要设置以下组件:
- GoogleStrategy:设置clientID,clientSecret和callbackURL
- verify / register callback function:接收用户数据,并完成后续用户认证和注册等流程
- session serialization / deserialization:将用户信息存储到cookie/从cookie解码用户信息
- next middleware(或router的callback function):执行后续api数据返回
创建google strategy
1 | // passport-config.js |
1 | // index.js |
cookie验证流程
Express-session middleware:用于解码从client发来的cookie
1
2
3
4
5
6
7
8
9// index.js
const session = require("express-session");
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
})
);passport.initialize() middleware:负责从cookie中提取userId
1
2// index.js
app.use(passport.initialize());app.use(passport.session()):使用userId,从数据库中获取user,或将user序列化为cookie
1
2
3
4
5
6
7
8
9
10
11
12
13// passport-config.js
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser(async (id, done) => {
try {
let user = await User.findById(id);
done(null, user);
} catch (error) {
done(error);
}
});1
2// index.js
app.use(passport.session())Self-defined middleware:使用
req.isAuthenticated()
函数验证登录1
2
3
4
5
6
7
8middlewares.checkAuthenticated = async (req, res, next) => {
if (req.isAuthenticated()) {
next();
} else {
req.flash("error", "You need to login first");
res.redirect("/auth/login");
}
}